﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace AutoParameter
{
    public static class PropertyMapper
    {
        public static Action<Type, Type> CreateMap(Type sourceType, Type targetType)
        {
            var sourceParam = Expression.Parameter(sourceType, "source");
            var targetParam = Expression.Parameter(targetType, "target");

            var block = new List<Expression>();

            foreach (var targetProp in targetType.GetProperties())
            {
                var targetAttr = targetProp.GetCustomAttribute<NodeAttribute>();
                if (targetAttr == null) continue;

                var sourceProp = sourceType.GetProperties()
                    .FirstOrDefault(p => p.GetCustomAttribute<NodeAttribute>()?.Name == targetAttr.Name &&
                                         p.PropertyType == targetProp.PropertyType);
                if (sourceProp == null) continue;

                var sourceValue = Expression.Property(sourceParam, sourceProp);
                var targetValue = Expression.Property(targetParam, targetProp);

                var assign = Expression.Assign(targetValue, sourceValue);
                block.Add(assign);
            }

            var body = Expression.Block(block);
            return Expression.Lambda<Action<Type, Type>>(body, sourceParam, targetParam).Compile();
        }

        public static Action<TSource, TTarget> CreateMap<TSource, TTarget>()
        {
            var sourceType = typeof(TSource);
            var targetType = typeof(TTarget);

            var sourceParam = Expression.Parameter(sourceType, "source");
            var targetParam = Expression.Parameter(targetType, "target");

            var block = new List<Expression>();

            foreach (var targetProp in targetType.GetProperties())
            {
                var targetAttr = targetProp.GetCustomAttribute<NodeAttribute>();
                if (targetAttr == null) continue;

                var sourceProp = sourceType.GetProperties()
                    .FirstOrDefault(p => p.GetCustomAttribute<NodeAttribute>()?.Name == targetAttr.Name &&
                                         p.PropertyType == targetProp.PropertyType);
                if (sourceProp == null) continue;

                var sourceValue = Expression.Property(sourceParam, sourceProp);
                var targetValue = Expression.Property(targetParam, targetProp);

                var assign = Expression.Assign(targetValue, sourceValue);
                block.Add(assign);
            }

            var body = Expression.Block(block);
            return Expression.Lambda<Action<TSource, TTarget>>(body, sourceParam, targetParam).Compile();
        }
    }
}
